from typing import Optional
import os
import ffmpeg

from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal
from PyQt5.QtWidgets import (
    QWidget, QLabel, QLineEdit, QPushButton, QGridLayout, QVBoxLayout,
    QHBoxLayout, QMessageBox, QProgressBar, QFileDialog
)

from app.core.ffmpeg_utils import get_ffmpeg_exe, get_ffprobe_exe


class VideoAudioMuxThread(QThread):
    finished = pyqtSignal(bool, str)

    def __init__(self, video_path: str, audio_path: str, output_path: str):
        super().__init__()
        self.video_path = video_path
        self.audio_path = audio_path
        self.output_path = output_path

    def run(self) -> None:
        try:
            exe = get_ffmpeg_exe()
            if not exe:
                raise FileNotFoundError("未找到 ffmpeg，可安装 imageio-ffmpeg 或将 ffmpeg 加入 PATH")
            if not os.path.exists(self.video_path):
                raise FileNotFoundError("视频文件不存在")
            if not os.path.exists(self.audio_path):
                raise FileNotFoundError("音频文件不存在")

            # 探测有无音频流，用于决定是否丢弃原音轨
            ffprobe_exe = get_ffprobe_exe() or 'ffprobe'
            probe = ffmpeg.probe(self.video_path, cmd=ffprobe_exe)
            has_audio = any(s.get('codec_type') == 'audio' for s in probe.get('streams', []))

            inp_v = ffmpeg.input(self.video_path)
            inp_a = ffmpeg.input(self.audio_path)

            # 将外部音频与视频合成。默认复制视频码流，重新编码音频为 AAC。
            # 若原视频已有音频，则丢弃原音频，仅使用外部音频。
            if has_audio:
                out = ffmpeg.output(
                    inp_v.video, inp_a.audio,
                    self.output_path,
                    vcodec='copy', acodec='aac', audio_bitrate='192k',
                    map='0:v:0', **{'map:1:a:0': None},
                    movflags='+faststart'
                )
            else:
                out = ffmpeg.output(
                    inp_v.video, inp_a.audio,
                    self.output_path,
                    vcodec='copy', acodec='aac', audio_bitrate='192k',
                    movflags='+faststart'
                )

            ffmpeg.run(out, overwrite_output=True, cmd=exe)
            self.finished.emit(True, self.output_path)
        except Exception as exc:
            self.finished.emit(False, str(exc))


class VideoAudioMuxWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._build_ui()
        self._wire()
        self.thread: Optional[VideoAudioMuxThread] = None
        self.sim_timer = QTimer(self)
        self.sim_timer.setInterval(150)
        self.sim_timer.timeout.connect(self._on_sim)
        self.simulating = False
        self.actual_done = False

    def _build_ui(self):
        self.video_edit = QLineEdit(self)
        self.video_btn = QPushButton("浏览视频", self)
        self.audio_edit = QLineEdit(self)
        self.audio_btn = QPushButton("浏览音频", self)
        self.output_edit = QLineEdit(self)
        self.output_btn = QPushButton("保存到", self)

        self.progress_bar = QProgressBar(self)
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)
        self.start_btn = QPushButton("开始合成", self)

        grid = QGridLayout()
        r = 0
        grid.addWidget(QLabel("视频文件:"), r, 0); grid.addWidget(self.video_edit, r, 1); grid.addWidget(self.video_btn, r, 2); r += 1
        grid.addWidget(QLabel("音频文件:"), r, 0); grid.addWidget(self.audio_edit, r, 1); grid.addWidget(self.audio_btn, r, 2); r += 1
        grid.addWidget(QLabel("输出视频:"), r, 0); grid.addWidget(self.output_edit, r, 1); grid.addWidget(self.output_btn, r, 2); r += 1

        lay = QVBoxLayout(self)
        lay.addLayout(grid)
        lay.addWidget(self.progress_bar)
        lay.addWidget(self.start_btn, 0, Qt.AlignRight)
        self.setLayout(lay)

    def _wire(self):
        self.video_btn.clicked.connect(self._pick_video)
        self.audio_btn.clicked.connect(self._pick_audio)
        self.output_btn.clicked.connect(self._pick_out)
        self.start_btn.clicked.connect(self._start)

    def _pick_video(self):
        p, _ = QFileDialog.getOpenFileName(self, "选择视频", "", "视频文件 (*.mp4 *.mkv *.mov *.avi *.flv *.wmv *.webm)")
        if p:
            self.video_edit.setText(p)

    def _pick_audio(self):
        p, _ = QFileDialog.getOpenFileName(self, "选择音频", "", "音频文件 (*.mp3 *.aac *.m4a *.wav *.flac)")
        if p:
            self.audio_edit.setText(p)

    def _pick_out(self):
        p, _ = QFileDialog.getSaveFileName(self, "保存输出", "", "视频文件 (*.mp4)")
        if p:
            self.output_edit.setText(p)

    def _start(self):
        if self.thread is not None and self.thread.isRunning():
            return
        v = self.video_edit.text().strip()
        a = self.audio_edit.text().strip()
        o = self.output_edit.text().strip()
        if not v:
            QMessageBox.warning(self, "提示", "请选择视频文件"); return
        if not a:
            QMessageBox.warning(self, "提示", "请选择音频文件"); return
        if not o:
            QMessageBox.warning(self, "提示", "请选择输出位置"); return

        self.progress_bar.setValue(0)
        self.simulating = True
        self.actual_done = False
        self.sim_timer.start()
        self.start_btn.setEnabled(False)

        self.thread = VideoAudioMuxThread(v, a, o)
        self.thread.finished.connect(self._done)
        self.thread.start()

    def _on_sim(self):
        if not self.simulating:
            return
        val = self.progress_bar.value()
        if self.actual_done:
            self.progress_bar.setValue(100)
            self.sim_timer.stop(); self.simulating = False; return
        if val < 97:
            from random import randint
            self.progress_bar.setValue(min(97, val + randint(1, 3)))

    def _done(self, ok: bool, msg: str):
        self.actual_done = True
        if ok:
            QMessageBox.information(self, "成功", f"合成完成！\n输出文件: {msg}")
        else:
            self.simulating = False; self.sim_timer.stop(); self.progress_bar.setValue(0)
            QMessageBox.critical(self, "错误", msg)
        self.start_btn.setEnabled(True)


